/**
 * iSC通用播放组件，当存在plugin.paramsFunc参数时，即为插件模式播放，否则为h5模式播放
 *
 * 一、配置参数说明：
 * plugin：插件模式相关配置对象
 *      streamMode: 可选，主子码流标识：0-主码流，1-子码流，默认为：1
 *      encodeSecret：可选，是否对secret参数加密，true：加密，false：不加密(不安全)。建议为true，默认为：true
 *      paramsFunc：插件模式时必选，预览或回放时，获取控件初始时必要参数的函数，回调参数：pubkey，当encodeSecret=true时，RSA公钥，用于服务端接口加密secret。
 *                  该函数需要返回Promise对象，resolve的参数对象必须存在以下属性，具体可见例子：
 *                  appkey(或appKey)、enc_secret(或encSecret、或secret)、service_ip(或serviceIp、或ip)、service_port(或servicePort、或port)
 *      buttonIds：可选，预览时，鼠标移入各个视频下方出现的功能按钮ID，默认无任何按钮
 * h5: h5模式相关配置对象
 *      urlFunc: h5模式时必选，预览时，用于获取websocket流地址的函数，
 *                   函数参数：cameraIndexCode(视频设备编码), protocol(websocket协议：wss或ws)
 *                   该函数需要返回Promise对象，resolve的参数为字符串：websocket流地址，具体可见例子
 *      ptzStartFunc: 可选，需要云台控制时才传。开始操作云台回调函数(鼠标按下时触发)，回调参数：cameraIndexCode, direction
 *      ptzEndFunc: 可选，需要云台控制时才传。结束操作云台回调函数(鼠标松开时触发)，回调参数：cameraIndexCode, direction
 * playMode：可选，初始播放模式，0:实时预览,1:回放，默认为：0
 * showToolbar：可选，是否显示工具栏(布局切换、全屏等等功能),true:显示，false:不显示，默认：true
 * cameraList：视频设备对象数组，每个对象必须包含 cameraIndexCode 属性，可选 state 属性(预览播放状态，0：待机, 1:播放中，-1：失败，99：加载中)
 * layout：可选，预览时的网格布局，如：'4x4'。不传时根据 cameraList 参数计算最合适的布局
 *
 * 二、事件：
 * ready: 播放控件初始完毕时触发
 * state-change：某个视频设备播放状态改变时触发，参数：视频设备对象
 * select：选中某个小窗口触发，参数：如果当前窗口存在某个视频设备，则为cameraIndexCode，否则：无参
 *
 * 三、方法：
 * startPreview(cameraIndexCode)：开始预览某个视频设备
 * stopPreview(cameraIndexCode)：停止预览某个视频设备
 * startBatchPreview(cameraIndexCodes)：批量预览视频设备, 参数：[逗号]分隔的字符串，或者数组
 * startPlayback(cameraIndexCode, startTime, endTime)：回放指定视频设备，参数：startTime、endTime 字符串或Date
 */
const iscVideoPlayer = {
    props: {
        plugin: {   //使用插件播放的相关配置参数
            type: Object,
            default: null
            /**
             * {
             *      streamMode: 1,
             *      encodeSecret: true,  //使用插件播放时，是否对secret参数加密(建议为true)
             *      paramsFunc: null, //获取播放参数的函数
             *      buttonIds: '',
             * }
             */
        },
        h5: {
            type: Object,
            default: null
        },
        playMode: {
            type: [Number, String],
            default: 0,
        },
        showToolbar: {
            type: Boolean,
            default: true,
        },
        cameraList: {   //摄像头列表
            type: Array,
            default: [] //{cameraIndexCode: ''}
        },
        layout: { //播放布局
            type: String,
            default: ''
        },
    },
    data() {
        return {}
    },
    computed: {
        /**
         * 是否启用插件播放(通过判断是否传了 plugin.paramsUrl参数)
         * @return {boolean}
         */
        pluginEnabled() {
            let {paramsFunc} = this.plugin || {};
            return !!paramsFunc;
        },
        playerInst() {
            if (this.pluginEnabled) {
                return this.$refs.pluginPlayer;
            } else {
                return this.$refs.h5Player;
            }
        }
    },
    template: `
    <div class="isc-video-player">
        <isc-video-plugin-player v-if="pluginEnabled" ref="pluginPlayer" v-bind="plugin" 
            :camera-list="cameraList" 
            :layout="layout" 
            :play-mode="playMode"
            :show-toolbar="showToolbar"
            @ready="playerReady"
            @state-change="cameraStateChange" 
            @select="cameraSelect"></isc-video-plugin-player>
        <isc-video-h5-player v-else ref="h5Player" v-bind="h5"
            :camera-list="cameraList" 
            :layout="layout" 
            :play-mode="playMode"
            :show-toolbar="showToolbar"
            @ready="playerReady"
            @state-change="cameraStateChange" 
            @select="cameraSelect"></isc-video-h5-player>
    </div>
    `,
    methods: {
        playerReady() {
            this.$emit('ready');
        },
        cameraStateChange(data) {
            this.$emit('state-change', data);
        },
        cameraSelect(data) {
            this.$emit('select', data);
        },
        /**
         * 供外部调用接口
         * 开始实时预览某个视频设备
         * @param cameraIndexCode
         */
        async startPreview(cameraIndexCode, undefined, isPlayback, startTime, endTime) {
            return this.playerInst.startPreview(cameraIndexCode, undefined, isPlayback, startTime, endTime);
        },
        /**
         * 供外部调用接口
         * 停止预览某个视频
         * @param cameraIndexCode
         */
        async stopPreview(cameraIndexCode) {
            return this.playerInst.stopPreview(cameraIndexCode);
        },
        /**
         * 供外部调用接口
         * 开始批量实时预览视频设备
         * @param cameraIndexCodes
         */
        async startBatchPreview(cameraIndexCodes, isPlayback) {
            return this.playerInst.startBatchPreview(cameraIndexCodes, isPlayback);
        },
        /**
         * 供外部调用接口
         * 回放指定视频设备
         * @param cameraIndexCode
         * @param startTime
         * @param endTime
         * @return {Promise<*>}
         */
        async startPlayback(cameraIndexCode, startTime, endTime) {
            return this.playerInst.startPlayback(cameraIndexCode, startTime, endTime);
        },
    }
};
Vue.component('isc-video-player', iscVideoPlayer);

/**
 * 两种播放模式基础类
 */
const _iscVideoBase = {
    props: {
        cameraList: {
            type: Array,
            default: []
        },
        layout: {
            type: String,
            default: ''
        },
        playMode: {
            type: [Number, String],
            default: 0,
        },
        showToolbar: {
            type: Boolean,
            default: true
        },
    },
    data() {
        return {
            width: 0,   //播放容器宽度
            height: 0,  //播放容器高度
            curPlayMode: 0,    //当前播放模式
            scriptLibPath: '',
            playerLayout: [],    //各个模式下有相应的配置
            oWebControl: null, //播放控件对象
            needResetLayout: false,
            selectWndId: 1, //当前选中窗口, 下标从1开始
            isIframe: false,   //是否在iframe中使用
        }
    },
    computed: {
        /**
         * 实际布局
         * @return {string|*}
         */
        realLayout() {
            if (this.layout) {   //如果传了布局参数，则使用该参数
                return this.layout;
            }
            let realLayout = '1x1';
            if (this.cameraList && this.cameraList.length > 0) {
                let cameraSum = this.cameraList.length;
                for (let item of this.playerLayout) {
                    let {num, layout} = item;
                    if (cameraSum <= num) {
                        realLayout = layout;
                        break;
                    }
                }
            }
            return realLayout;
        },
        /**
         * 总窗口数量
         * @return {number}
         */
        wndCount() {
            let mt = this.realLayout.match(/(\d+)([x*+])(\d+)/);
            let first = parseInt(mt[1]);
            let op = mt[2];
            let second = parseInt(mt[3]);
            if (op === '+') {
                return first + second;
            } else {
                return first * second;
            }
        },
        /**
         * 摄像头编码 --> 摄像头对象
         * @return {Map<any, any>}
         */
        cameraCodeMap() {
            let map = new Map();
            this.cameraList.forEach(camera => {
                map.set(camera.cameraIndexCode, camera);
            });
            return map;
        },
        /**
         * 播放窗口编号 --> 该窗口的摄像头对象
         * @return {Map<any, any>}
         */
        wndIdCameraMap() {
            let map = new Map();
            this.cameraList.forEach(camera => {
                let wndId = camera._wndId;
                if (wndId && wndId > 0) {
                    map.set(wndId, camera);
                }
            });
            return map;
        },
        /**
         * 当前选中的视频设备
         * @return {null|*}
         */
        selectedCamera() {
            if (this.selectWndId && this.cameraList && this.cameraList.length > 0) {
                return this.cameraList.find(camera => camera._wndId === this.selectWndId);
            }
            return null;
        },
        /**
         * 播放失败状态的视频设备
         * @return {*}
         */
        previewFailedCameras() {
            return this.cameraList
                .filter(camera => camera.state === -1);
        },
        /**
         * 加载中的视频设备
         * @return {*}
         */
        previewLoadingCameras() {
            return this.cameraList
                .filter(camera => camera.state === 99);
        }
    },
    created() {
        this.isIframe = window.self !== window.top;
        iscUtil.createStyle();
        this.scriptLibPath = iscUtil.getCurJsPath();
        this.curPlayMode = Number(this.playMode);
        if ((!this.cameraList || this.cameraList.length === 0) && this.curPlayMode === 0) {
            this.needResetLayout = true;    //初始组件时，没有设置视频设备列表，则需要watch cameraList 改变，重新设置布局
        }
    },
    watch: {
        cameraList: {
            handler(list) {
                if (this.needResetLayout) {
                    this.resetLayout();
                }
                if (list) {  //初始预览播放状态，以及播放窗口编号 属性
                    list.forEach(camera => {
                        if (Object.prototype.hasOwnProperty.call(camera, 'state')) {
                            camera.state = 0;
                        } else {
                            this.$set(camera, 'state', 0);
                        }
                        if (Object.prototype.hasOwnProperty.call(camera, '_wndId')) {
                            camera._wndId = 0;
                        } else {
                            this.$set(camera, '_wndId', 0);
                        }
                    });
                }
            },
            immediate: true
        },
        /**
         * 监视选中窗口编号
         */
        selectWndId(wndId) {
            //console.log('selectWndId ==> ', wndId)
            let camera = this.wndIdCameraMap.get(wndId);
            if (camera) {
                this.emitSelectCamera(camera.cameraIndexCode);
            } else {
                this.emitSelectCamera(null);
            }
        }
    },
    methods: {
        /**
         * 根据播放模式，加载必要的js
         * @param pluginEnabled
         * @param encodeSecret
         */
        loadRequiredScripts(pluginEnabled, encodeSecret) {
            let scripts = [];
            if (pluginEnabled) {
                /*if(encodeSecret) {
                    scripts.push(this.scriptLibPath + './jsencrypt.min.js');
                }*/
                if (!window.WebControl) {
                    scripts.push(this.scriptLibPath + './plugin/jsWebControl-1.0.0.min.js');
                }
            } else {
                if (!window.JSPlugin) {
                    scripts.push(this.scriptLibPath + './h5/h5player.min.js');
                }
            }
            if (scripts.length > 0) {
                iscUtil.dynamicLoadScripts(scripts).then(() => {
                    //console.log('播放js加载完毕');
                    this.$nextTick(() => {
                        this.initPlayer()
                            .then(() => {
                                this.$emit('ready')
                            }).catch(e => {
                        });
                    });
                });
            } else {
                this.$nextTick(() => {
                    this.initPlayer().then(() => {
                        this.$emit('ready')
                    }).catch(e => {
                    });
                });
            }
        },
        /**
         * 变更播放状态
         * @param cameraIndexCode
         * @param state
         * @param wndId
         */
        changeCameraState(cameraIndexCode, state, wndId) {
            let camera = this.cameraCodeMap.get(cameraIndexCode);
            if (!camera && wndId) {
                camera = this.wndIdCameraMap.get(wndId);
            }
            if (camera) {
                if (camera.state !== state) {
                    camera.state = state;
                    if (state === 99) {    //加载中时，设置一下摄像头在哪个窗口尝试播放
                        camera._wndId = wndId;
                    } else if (state === 0) {
                        if (this.selectWndId === camera._wndId) {
                            this.emitSelectCamera(null);//往外抛 没选中任何视频设备
                        }
                        camera._wndId = 0;
                    }
                    this.$emit('state-change', {cameraIndexCode: camera.cameraIndexCode, state});
                }
            }
        },
        /**
         * 初始视频播放器(由各自播放方式实现)
         */
        initPlayer() {
        },
        /**
         * 重新设置播放网格布局
         */
        resetLayout() {
        },
        /**
         * 在预览指定摄像头前，用来做一些清理操作等
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @param wndId
         * @return {Promise<boolean>}
         */
        async beforePreview(cameraIndexCode, wndId) {
            return true;
        },
        /**
         * 真正开始尝试预览某个摄像头
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @param wndId
         * @return {Promise<boolean>}
         */
        async doPreview(cameraIndexCode, wndId) {
            return true;
        },
        /**
         * 真正开始尝试批量预览
         * @param cameraIndexCodeWndIdArr [{cameraIndexCode, wndId}]
         * @return {Promise<boolean>}
         */
        async doBatchPreview(cameraIndexCodeWndIdArr) {
            return true;
        },
        /**
         * 停止预览操作
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @return {Promise<boolean>}
         */
        async doStopPreview(cameraIndexCode) {
            return true;
        },
        /**
         * 在回放指定摄像头前，用来做一些清理操作等
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @return {Promise<boolean>}
         */
        async beforePlayback(cameraIndexCode) {
            return true;
        },
        /**
         * 真正开始尝试回放某个摄像头
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @param statTimestamp
         * @param endTimestamp
         * @return {Promise<boolean>}
         */
        async doPlayback(cameraIndexCode, statTimestamp, endTimestamp) {
            return true;
        },
        /**
         * 获取可预览的窗口ID
         * @param wndId
         * @return {*}
         */
        getNextCanPlayWndId(wndId) {
            let tmpCamera = this.wndIdCameraMap.get(wndId);
            if (tmpCamera) {
                return this.getNextCanPlayWndId(wndId + 1);   //递归获取下一个可预览的窗口ID
            } else {
                return wndId;
            }
        },
        /**
         * 往组件外抛摄像头选中事件
         * @param cameraIndexCode
         */
        emitSelectCamera(cameraIndexCode) {
            this.$emit('select', cameraIndexCode);
        },
        /**
         * 供外部调用接口
         * 开始实时预览某个视频设备
         * 可指定窗口编号，如不指定，则取当前选中的窗口
         * @param cameraIndexCode
         * @param wndId
         */
        async startPreview(cameraIndexCode, wndId, isPlayback, startTime, endTime) {
            let canPlay = await this.beforePreview(cameraIndexCode, wndId);
            if (canPlay && cameraIndexCode) {
                let playWndId = wndId ? wndId : this.selectWndId;
                let oldCamera = this.wndIdCameraMap.get(playWndId);
                if (oldCamera) { //要播放的窗口有另一个摄像头
                    if (oldCamera.cameraIndexCode === cameraIndexCode) {   //为自身，不做任何操作
                        return true;
                    } else {  //停止该摄像头(如果在播放)，并设置待机状态，然后继续播放新的摄像头
                        await this.stopPreview(oldCamera.cameraIndexCode);
                    }
                }
                let camera = this.cameraCodeMap.get(cameraIndexCode);
                if (camera._wndId) {
                    throw new Error('该摄像头已在其他窗口播放');
                }
                this.changeCameraState(cameraIndexCode, 99, playWndId);
                this.emitSelectCamera(cameraIndexCode);
                return await this.doPreview(cameraIndexCode, playWndId, isPlayback, startTime, endTime);
            } else {
                return true;
            }
        },
        /**
         * 供外部调用接口
         * 开始批量实时预览视频设备
         * @param cameraIndexCodes
         */
        async startBatchPreview(cameraIndexCodes, isPlayback) {

            await this.beforePreview(cameraIndexCodes, null);
            if (cameraIndexCodes) {
                if (typeof cameraIndexCodes === 'string') {
                    cameraIndexCodes = cameraIndexCodes.split(',');
                }
                if (cameraIndexCodes.length > 0) {
                    let filteredCodes = cameraIndexCodes.filter(code => {   //过滤掉已经在窗口中预览的
                        let camera = this.cameraCodeMap.get(code);
                        return !camera._wndId;
                    });
                    if (filteredCodes.length > 0) {
                        let cameraIndexCodeWndIdArr = [];
                        let playWndId = 1;
                        for (let code of filteredCodes) {
                            playWndId = this.getNextCanPlayWndId(playWndId);
                            if (playWndId > this.wndCount) {    //超过了可播放窗口数量
                                break;
                            }
                            cameraIndexCodeWndIdArr.push({cameraIndexCode: code, wndId: playWndId});
                            this.changeCameraState(code, 99, playWndId);
                            playWndId = playWndId + 1;
                        }
                        if (cameraIndexCodeWndIdArr.length > 0) {
                            return this.doBatchPreview(cameraIndexCodeWndIdArr, isPlayback);
                        }
                    }
                }
            }
            return true;
        },
        /**
         * 供外部调用接口
         * 开始回放指定视频设备
         * @param cameraIndexCode
         * @param startTime
         * @param endTime
         * @return {Promise<void>}
         */
        async startPlayback(cameraIndexCode, startTime, endTime) {
            await this.beforePlayback(cameraIndexCode);
            this.cameraList.forEach(camera => {   //所欲设备在此处重置为 待机状态
                this.changeCameraState(camera.cameraIndexCode, 0, 0);
            });
            let statTimestamp = typeof startTime === 'string' ? new Date(startTime.replace(/-/ig, "/")).getTime() : startTime.getTime();
            let endTimestamp = typeof endTime === 'string' ? new Date(endTime.replace(/-/ig, "/")).getTime() : endTime.getTime();
            if (endTimestamp <= startTime) {
                throw new Error('回放开始时间必须小于结束时间');
            }
            return this.doPlayback(cameraIndexCode, statTimestamp, endTimestamp);
        },
        /**
         * 供外部调用接口
         * 停止预览某个视频
         * @param cameraIndexCode
         */
        async stopPreview(cameraIndexCode) {
            await this.doStopPreview(cameraIndexCode);
            this.changeCameraState(cameraIndexCode, 0, 0);
            return true;
        }
    }
};

/**
 * 插件播放组件
 * @private
 */
const _iscVideoPluginPlayer = {
    props: {
        encodeSecret: {  //插件播放时,是否对secret参数加密(建议为true)
            type: Boolean,
            default: true
        },
        paramsFunc: {      //插件播放时通过该函数获取播放必要的参数
            type: Function,
            required: true
        },
        buttonIds: {
            type: [Array, String],
            default: ''
        },
        streamMode: {
            type: Number,
            default: 1,
        }
    },
    data() {
        return {
            containerId: 'iscPluginPlayer',
            pluginLoaded: false,
            playerMsg: '视频控件加载中，请稍后',

            playerLayout: [{num: 1, layout: "1x1"}, {num: 2, layout: "1x2"}, {num: 3, layout: "1+2"}, {
                num: 4,
                layout: "2x2"
            }, {num: 6, layout: "1+5"}, {num: 7, layout: "3+4"}, {num: 9, layout: "3x3"}, {
                num: 13,
                layout: "4+9"
            }, {num: 16, layout: "4x4"}, {num: 24, layout: "4x6"}, {num: 25, layout: "5x5"}],
            snapDir: 'D:\\SnapDir',     //抓图存储路径
            videoDir: 'D:\\VideoDir',   //紧急录像或录像剪辑存储路径
            playbackToolbarBtnIds: '2050,2304,2306,2307,2308,2309,4098',   //回放时控件工具栏按钮
            parentTitle: '',    //iframe中使用时的父页面标题
            iframePos: {},      // iframe相对文档的位置
            iframeClientPos: null,  // iframe相对视窗的位置
            iframeParentShowSize: null, // 视窗大小 width height
        }
    },
    computed: {
        curButtonIds() {
            if (this.buttonIds) {
                if (Array.isArray(this.buttonIds)) {
                    return this.buttonIds.join(',');
                } else {
                    return this.buttonIds;
                }
            }
            return '';
        }
    },
    template: `
    <div class="isc-common-player isc-plugin-player" ref="iscPluginPlayer">
        <div :id="containerId" class="video-container"></div>
        <div v-if="!pluginLoaded" class="player-msg">{{playerMsg}}</div>
    </div>
    `,
    mounted() {
        if (this.isIframe) {
            window.addEventListener('message', this.iframeEventHandler);
        }
        this.loadRequiredScripts(true, this.encodeSecret);
    },
    methods: {
        /**
         * 初始播放器
         * 会在loadRequiredScripts 完毕后执行
         * @return {Promise<unknown>}
         */
        initPlayer() {
            return new Promise((resolve, reject) => {
                if (this.paramsFunc) {
                    let playerRect = this.$refs.iscPluginPlayer.getBoundingClientRect();
                    this.width = playerRect.width;
                    this.height = playerRect.height;
                    this.initPlugin().then(() => {
                        resolve();
                    }).catch(error => {
                        if (error) {
                            if (typeof error === 'string') {
                                this.playerMsg = error;
                            } else if (error instanceof Error) {
                                this.playerMsg = error.message;
                            }
                        }
                        reject(error);
                    });
                } else {
                    this.playerMsg = '缺少获取播放参数的函数：params-func';
                    reject();
                }
            });
        },
        /**
         * 初始播放控件
         * @return {Promise<unknown>}
         */
        initPlugin() {
            let thiz = this;
            return new Promise((resolve, reject) => {
                thiz.oWebControl = new WebControl({
                    szPluginContainer: thiz.containerId,                       // 指定容器id
                    iServicePortStart: 15900,                           // 指定起止端口号，建议使用该值
                    iServicePortEnd: 15909,
                    szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11",   // 用于IE10使用ActiveX的clsid
                    cbConnectSuccess: function () {                     // 创建WebControl实例成功
                        thiz.oWebControl.JS_StartService("window", {         // WebControl实例创建成功后需要启动服务
                            dllPath: "./VideoPluginConnect.dll"         // 值"./VideoPluginConnect.dll"写死
                        }).then(function () {                           // 启动插件服务成功
                            window.addEventListener('resize', thiz.resizePlayer);
                            window.addEventListener('scroll', thiz.setWndCover);
                            thiz.oWebControl.JS_SetWindowControlCallback({   // 设置消息回调
                                cbIntegrationCallBack: thiz.cbIntegrationCallBack
                            });
                            let createWndCfg = thiz.isIframe ? {
                                cbSetDocTitle: function (uuid) {
                                    thiz.oWebControl._pendBg = false;
                                    thiz.parentFrameUpdateTitle(uuid);
                                }
                            } : null;
                            thiz.oWebControl.JS_CreateWnd(thiz.containerId, thiz.width, thiz.height, createWndCfg).then(function () { //JS_CreateWnd创建视频播放窗口，宽高可设定
                                if (thiz.isIframe) {
                                    thiz.parentFrameUpdateTitle(thiz.parentTitle);
                                    window.parent.postMessage({
                                        action: 'updatePos',
                                        msg: '更新Pos'
                                    }, '\*');
                                }
                                thiz.initPlayMode(thiz.curPlayMode).then(() => {
                                    thiz.pluginLoaded = true;
                                    resolve();  // 创建播放实例成功后初始化
                                }).catch(e => {
                                    thiz.oWebControl.JS_HideWnd();
                                    thiz.playerMsg = e.message;
                                })
                            });
                        }, function () { // 启动插件服务失败
                            console.error("启动播放插件服务失败");
                            reject(new Error("启动播放插件服务失败"));
                        });
                    },
                    cbConnectError: function () { // 创建WebControl实例失败
                        thiz.oWebControl = null;
                        if (confirm("您还未安装过插件，请下载VideoWebPlugin_V1.5.1.exe并进行安装！\n是否立即下载？")) {
                            thiz.downloadPlugin();
                        }
                        reject();
                    },
                    cbConnectClose: function (bNormalClose) {
                        thiz.oWebControl = null;
                    }
                });
                window.oWebControl = thiz.oWebControl;
            });
        },
        /**
         * 在iframe中使用时的消息事件处理函数
         * @param e
         */
        iframeEventHandler(e) {
            //console.log('iframeEventHandler', e);
            if (e && e.data) {
                switch (e.data.action) {
                    case 'sendTitle':              // 父页面将其标题发送过来，子页面保存该标题，以便创建插件窗口成功后将标题设置回给父页面
                        this.parentTitle = e.data.info;
                        break;
                    case 'updatePos':              // 更新插件位置：JS_CreateWnd时需要父页面计算滚动条偏移量，初始偏移量叠加该偏移量作为iframe的偏移量，防止插件窗口与DIV窗口初始不贴合情况
                        let scrollValue = e.data.scrollValue;     // 滚动条滚动偏移量
                        this.oWebControl.JS_SetDocOffset({
                            left: this.iframeClientPos.left,
                            top: this.iframeClientPos.top
                        });  // 更新插件窗口位置
                        this.resizePlayer();
                        this.setWndCover();
                        break;
                    case 'updateInitParam':
                        this.iframePos = e.data.iframeOffset;             // iframe与文档的偏移量
                        this.iframeClientPos = e.data.iframeClientPos;    // iframe相对视窗的位置
                        this.iframeParentShowSize = e.data.showSize;      // 视窗大小
                        break;
                    case 'resize':
                        this.iframeParentShowSize = e.data.showSize;    // 视窗大小
                        this.iframePos = e.data.iframeOffset;           // iframe与文档的偏移量
                        this.iframeClientPos = e.data.iframeClientPos;  // iframe相对视窗的位置
                        this.setWndCover();
                        break;
                    case 'scroll':
                        this.iframeParentShowSize = e.data.showSize;   // 视窗大小
                        this.iframePos = e.data.iframeOffset;          // iframe与文档的偏移量
                        this.iframeClientPos = e.data.iframeClientPos; // iframe相对视窗的位置
                        let scrollValue2 = e.data.scrollValue;     // 滚动条滚动偏移量
                        if (this.oWebControl) {
                            this.oWebControl.JS_SetDocOffset({
                                left: this.iframeClientPos.left,
                                top: this.iframeClientPos.top
                            });    // 更新插件窗口位置
                            this.resizePlayer();
                            this.setWndCover();
                        }
                        break;
                    default:
                        break;
                }
            }
        },
        parentFrameUpdateTitle(title) {
            window.parent.postMessage({
                action: 'updateTitle',
                msg: '子页面通知父页面修改title',
                info: title
            }, '\*');    // '\*'表示跨域参数，请结合自身业务合理设置
        },
        /**
         * 初始预览
         */
        initPreview() {
            return this.initPlayMode(0);
        },
        /**
         * 初始回放
         * @return {Promise<*>}
         */
        initPlayback() {
            return this.initPlayMode(1);
        },
        /**
         * 初始 预览 或 回放
         * @param playMode 0:预览，1:回放
         * @return {Promise<unknown>}
         */
        async initPlayMode(playMode) {
            let pubKey;
            if (this.encodeSecret) {
                pubKey = await this.getPubKey();
            }
            let playParams = await this.paramsFunc(pubKey);
            if (playParams && typeof playParams === 'object') {
                let appkey = playParams.appkey || playParams.appKey;
                if (!appkey) throw new Error('缺少播放参数：appkey');
                let secret = playParams.enc_secret || (playParams.encSecret || playParams.secret);
                if (!secret) throw new Error('缺少播放参数：secret');
                let ip = playParams.service_ip || (playParams.serviceIp || playParams.ip);
                if (!ip) throw new Error('缺少播放参数：ip');
                let port = playParams.service_port || (playParams.servicePort || playParams.port);
                if (!port) throw new Error('缺少播放参数：port');
                port = parseInt(port);
                let thiz = this;
                return new Promise((resolve, reject) => {
                    let argumentConfig = {
                        playMode: playMode,
                        appkey: appkey,
                        secret: secret,
                        ip: ip,
                        port: port,
                        encryptedFields: this.encodeSecret ? 'secret' : '',
                        snapDir: this.snapDir,                          //抓图存储路径
                        videoDir: this.videoDir,                        //紧急录像或录像剪辑存储路径
                        layout: playMode === 0 ? this.realLayout : '1x1', //布局
                        enableHTTPS: 1,                                 //是否启用HTTPS协议，这里总是填1
                        showToolbar: this.showToolbar ? 1 : 0,          //是否显示工具栏
                        showSmart: 1,                                   //是否显示智能信息
                        buttonIDs: this.curButtonIds                    //自定义工具条按钮
                    };
                    if (playMode === 1) {
                        argumentConfig.toolBarButtonIDs = thiz.playbackToolbarBtnIds;
                    }
                    this.oWebControl.JS_RequestInterface({
                        funcName: "init",
                        argument: JSON.stringify(argumentConfig)
                    }).then((oData) => {
                        setTimeout(() => {
                            thiz.oWebControl.JS_Resize(thiz.width, thiz.height);  // 初始化后resize一次，规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
                        }, 300);
                        resolve();
                    }, () => {
                        reject(new Error('初始控件播放模式失败'));
                    });
                });
            } else {
                throw new Error('获取播放参数失败');
            }
        },
        /**
         * 停止所有预览
         * @return {Promise<unknown>}
         */
        doStopAllPreview() {
            return new Promise((resolve, reject) => {
                if (this.oWebControl) {
                    this.oWebControl.JS_RequestInterface({
                        funcName: "stopAllPreview"
                    }).then(function (oData) {
                        resolve();
                    });
                } else {
                    resolve();
                }
            });
        },
        /**
         * 停止所有回放
         * @return {Promise<unknown>}
         */
        doStopAllPlayback() {
            return new Promise((resolve, reject) => {
                if (this.oWebControl) {
                    this.oWebControl.JS_RequestInterface({
                        funcName: "stopAllPlayback"
                    }).then(function (oData) {
                        resolve();
                    });
                } else {
                    resolve();
                }
            });
        },
        /**
         * 控件 反 初始
         */
        uninit() {
            return new Promise((resolve, reject) => {
                if (this.oWebControl) {
                    this.oWebControl.JS_RequestInterface({
                        funcName: "uninit"
                    }).then(function (oData) {
                        resolve();
                    });
                } else {
                    resolve();
                }
            });
        },
        /**
         * 获取插件公钥
         * @return {Promise<unknown>}
         */
        getPubKey() {
            return new Promise((resolve, reject) => {
                this.oWebControl.JS_RequestInterface({
                    funcName: "getRSAPubKey",
                    argument: JSON.stringify({
                        keyLength: 1024
                    })
                }).then((oData) => {
                    if (oData.responseMsg.data) {
                        resolve(oData.responseMsg.data);
                    } else {
                        reject();
                    }
                })
            });
        },
        /**
         * 根据播放容器大小，重置控件大小
         */
        resizePlayer() {
            let playerRect = this.$refs.iscPluginPlayer.getBoundingClientRect();
            this.width = playerRect.width;
            this.height = playerRect.height;
            this.oWebControl.JS_Resize(playerRect.width, playerRect.height);
        },
        /**
         * 设置窗口裁剪，当因滚动条滚动导致窗口需要被遮住的情况下需要调用JS_CuttingPartWindow裁剪部分窗口
         */
        setWndCover() {
            let iWidth = document.body.clientWidth;
            let iHeight = document.body.clientHeight;
            let playerRect = this.$refs.iscPluginPlayer.getBoundingClientRect();

            let iCoverLeft = (playerRect.left < 0) ? Math.abs(playerRect.left) : 0;
            let iCoverTop = (playerRect.top < 0) ? Math.abs(playerRect.top) : 0;
            let iCoverRight = (playerRect.right - iWidth > 0) ? Math.round(playerRect.right - iWidth) : 0;
            let iCoverBottom = (playerRect.bottom - iHeight > 0) ? Math.round(playerRect.bottom - iHeight) : 0;

            iCoverLeft = (iCoverLeft > this.width) ? this.width : iCoverLeft;
            iCoverTop = (iCoverTop > this.height) ? this.height : iCoverTop;
            iCoverRight = (iCoverRight > this.width) ? this.width : iCoverRight;
            iCoverBottom = (iCoverBottom > this.height) ? this.height : iCoverBottom;

            /*if(this.isIframe && this.iframeClientPos){
                if(this.iframeClientPos.top<0 && Math.abs(this.iframeClientPos.top) > iCoverTop) {
                    iCoverTop = Math.abs(this.iframeClientPos.top) - iCoverTop;
                }
                if (this.iframeClientPos.left < 0 && Math.abs(this.iframeClientPos.left) > iCoverLeft){
                    iCoverLeft = Math.abs(this.iframeClientPos.left) - iCoverLeft;
                }
                // 判断剪切矩形的右边距
                let W1 = iWidth - iCoverRight;
                let W2 = this.iframeParentShowSize.width - this.iframeClientPos.left;
                if (W2 < iWidth){
                    iCoverRight = iWidth - W2 - W1;
                }
                // 判断剪切矩形的下边距
                let H1 = this.iframeClientPos.bottom - this.iframeParentShowSize.height;
                let H2 = iHeight - iCoverBottom;
                iCoverBottom = H1 - H2;
            }*/

            this.oWebControl.JS_RepairPartWindow(0, 0, this.width + 1, this.height);    // 多1个像素点防止还原后边界缺失一个像素条
            if (iCoverLeft != 0) {
                this.oWebControl.JS_CuttingPartWindow(0, 0, iCoverLeft, this.height);
            }
            if (iCoverTop != 0) {
                this.oWebControl.JS_CuttingPartWindow(0, 0, this.width + 1, iCoverTop);    // 多剪掉一个像素条，防止出现剪掉一部分窗口后出现一个像素条
            }
            if (iCoverRight != 0) {
                this.oWebControl.JS_CuttingPartWindow(this.width - iCoverRight, 0, iCoverRight, this.height);
            }
            if (iCoverBottom != 0) {
                this.oWebControl.JS_CuttingPartWindow(0, this.height - iCoverBottom, this.width, iCoverBottom);
            }
        },
        /**
         * 调用组件，根据视频数量，重置布局
         */
        resetLayout() {
            this.oWebControl.JS_RequestInterface({
                funcName: "setLayout",
                argument: JSON.stringify({
                    layout: this.realLayout
                })
            }).then((oData) => {
            })
        },
        downloadPlugin() {
            location.href = 'https://cdn.img.zjtpyun.com/clients/thirdtools/VideoWebPlugin_V1.5.1.exe';
        },
        /**
         * 控件事件消息回调
         * 设备播放失败时，点工具栏的全部关闭，不会触发消息回调，应该是控件的BUG
         * @param oData
         */
        cbIntegrationCallBack(oData) {
            let data = oData.responseMsg;
            let {type, msg} = data;
            //console.log('cbIntegrationCallBack ==> ', type, msg)
            if (this.curPlayMode === 0) { //只处理预览
                let {cameraIndexCode, result, wndId} = msg;
                switch (type) {
                    case 1: //窗口选中消息
                        this.selectWndId = msg.wndId;
                        break;
                    case 2: //预览/回放播放消息
                        if (result == 768) {    //开始播放
                            this.changeCameraState(cameraIndexCode, 1, wndId);
                        } else if (result == 816) {  //播放结束
                            this.changeCameraState(cameraIndexCode, 0, wndId);
                        } else { //播放失败、播放异常
                            this.changeCameraState(cameraIndexCode, -1, wndId);
                        }
                        break;
                    default:
                        break;
                }
            }
        },
        /**
         * 在预览指定摄像头前，用来做一些清理操作等
         * @return {Promise<Boolean>}
         */
        async beforePreview(cameraIndexCode, wndId) {
            if (this.curPlayMode === 1) {  //处于回放状态，销毁回放，初始预览
                await this.doStopAllPlayback();
                await this.uninit();
                await this.initPreview();
                this.curPlayMode = 0;
            }
            return true;
        },
        /**
         * 真正开始尝试预览某个摄像头
         * @param cameraIndexCode
         * @param playWndId
         * @return {Promise<Boolean>}
         */
        async doPreview(cameraIndexCode, playWndId) {
            return new Promise((resolve, reject) => {
                this.oWebControl.JS_RequestInterface({
                    funcName: "startPreview",
                    argument: JSON.stringify({
                        //authUuid: cameraInfo.authId+"",
                        cameraIndexCode: cameraIndexCode,                //监控点编号
                        streamMode: this.streamMode,                    //主子码流标识
                        transMode: 1,                                   //传输协议：0-UDP，1-TCP
                        gpuMode: 0,                                     //是否启用GPU硬解，0-不启用，1-启用
                        wndId: playWndId                                //可指定播放窗口
                    })
                }).then(function (oData) {
                    resolve(true);
                });
            });
        },
        /**
         * 停止预览操作
         * 控件未提供相应接口，直接返回true
         * @param cameraIndexCode
         * @return {Promise<boolean>}
         */
        async doStopPreview(cameraIndexCode) {
            return new Promise((resolve, reject) => {
                let camera = this.cameraCodeMap.get(cameraIndexCode);
                if (camera && camera._wndId) {
                    this.oWebControl.JS_RequestInterface({
                        funcName: "stopMultiPlay",
                        argument: JSON.stringify({
                            list: [{wndId: camera._wndId}]
                        })
                    }).then(function (oData) {
                        resolve(true);
                    });
                } else {
                    resolve(true);
                }
            });
        },
        /**
         * 真正开始尝试批量预览
         * @param cameraIndexCodeWndIdArr [{cameraIndexCode, wndId}]
         * @return {Promise<boolean>}
         */
        async doBatchPreview(cameraIndexCodeWndIdArr) {
            let list = cameraIndexCodeWndIdArr.map(obj => {
                let {cameraIndexCode, wndId} = obj;
                return {
                    cameraIndexCode: cameraIndexCode,
                    streamMode: this.streamMode,
                    transMode: 1,                           //传输协议
                    gpuMode: 0,
                    wndId: wndId
                };
            });
            return new Promise(resolve => {
                if (this.oWebControl) {
                    this.oWebControl.JS_RequestInterface({
                        funcName: "startMultiPreviewByCameraIndexCode",
                        argument: JSON.stringify({
                            list: list
                        })
                    }).then(function (oData) {
                        resolve(true);
                    });
                } else {
                    resolve(true);
                }
            });
        },
        /**
         * 在回放指定摄像头前，用来做一些清理操作等
         * @param cameraIndexCode
         * @return {Promise<boolean>}
         */
        async beforePlayback(cameraIndexCode) {
            if (this.curPlayMode === 0) {  //处于预览状态，销毁预览，初始回放
                await this.doStopAllPreview();
                await this.uninit();
                await this.initPlayback();
                this.curPlayMode = 1;
            } else {
                await this.doStopAllPlayback(); //只允许一个回放
            }
            return true;
        },
        /**
         * 真正开始尝试回放某个摄像头
         * @param cameraIndexCode
         * @param statTimestamp
         * @param endTimestamp
         * @return {Promise<boolean>}
         */
        async doPlayback(cameraIndexCode, statTimestamp, endTimestamp) {
            return new Promise(resolve => {
                if (this.oWebControl) {
                    this.oWebControl.JS_RequestInterface({
                        funcName: "startPlayback",
                        argument: JSON.stringify({
                            cameraIndexCode: cameraIndexCode,                   //监控点编号
                            startTimeStamp: Math.floor(statTimestamp / 1000).toString(),  //录像查询开始时间戳，单位：秒
                            endTimeStamp: Math.floor(endTimestamp / 1000).toString(),      //录像结束开始时间戳，单位：秒
                            recordLocation: 1,                     //录像存储类型：0-中心存储，1-设备存储
                            transMode: 1,                               //传输协议：0-UDP，1-TCP
                            gpuMode: 0,                                   //是否启用GPU硬解，0-不启用，1-启用
                            wndId: -1                                         //可指定播放窗口
                        })
                    }).then(function (oData) {
                        resolve(true);
                    });
                } else {
                    resolve(true);
                }
            });
        },
    },
    beforeDestroy() {
        if (this.isIframe) {
            window.removeEventListener('message', this.iframeEventHandler);
        }
        if (this.oWebControl != null) {
            this.oWebControl.JS_HideWnd();   // 先让窗口隐藏，规避可能的插件窗口滞后于浏览器消失问题
            this.oWebControl.JS_Disconnect().then(function () {  // 断开与插件服务连接成功
                },
                function () {  // 断开与插件服务连接失败
                });
        }
        window.removeEventListener('resize', this.resizePlayer);
        window.removeEventListener('scroll', this.setWndCover);
    },
    mixins: [_iscVideoBase]
};
Vue.component('isc-video-plugin-player', _iscVideoPluginPlayer);

/**
 * H5 单个窗口工具栏封装组件，目前仅包括 云台功能
 * 是对H5 播放模式组件的功能分隔，方便维护
 * @private
 */
const _iscVideoH5WndBar = {
    props: {
        ptzStartFunc: {
            type: Function,
            default: null,
        },
        ptzEndFunc: {
            type: Function,
            default: null,
        }
    },
    data() {
        return {
            ptzWndIndexes: new Set(),    //开启云台控制的窗口索引集合
        }
    },
    computed: {
        /**
         * 是否需要云台操作
         */
        needPTZ() {
            if (this.ptzStartFunc && typeof this.ptzStartFunc === 'function' && this.ptzEndFunc && typeof this.ptzEndFunc === 'function') {
                return true;
            }
            return false;
        },
    },
    methods: {
        /**
         * 初始某个窗口的底部工具栏
         */
        initPlayerWndBar(wndIndex) {
            if (this.needPTZ) {
                let barDom = document.querySelector('#player-wnd-bar-' + wndIndex);
                if (!barDom) {
                    barDom = document.createElement('div');
                    barDom.id = 'player-wnd-bar-' + wndIndex;
                    barDom.className = 'player-wnd-bar';
                    barDom.innerHTML = `
                    <div></div><!--左侧按钮组-->
                    <div style="display: flex; height: 100%; align-items: center; padding-right: 5px;"><!--右侧按钮组-->
                        <div class="bar-btn" id="barPtzBtn-${wndIndex}" data-active="0" title="云台控制">
                            <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink">
                                <path d="M960 959.232 0 960l128.896-320L320 640l0 64L172.224 704l-77.44 191.936 770.56-0.32L788.416 704 640 704l0-64 191.808 0L960 959.232zM256 371.136 256 268.8c0-112.768 100.288-204.48 224-204.48S704 156.032 704 268.8l0 102.336c0 102.72-64 187.2-192 201.472L512 832 448 832 448 572.608C320 558.272 256 473.856 256 371.136zM320 371.136C320 448.64 391.616 511.68 480 511.68S640 448.64 640 371.136L640 268.8c0-77.504-71.616-140.608-160-140.608S320 191.296 320 268.8L320 371.136z" fill="#ffffff"></path>
                            </svg>
                        </div>
                    </div>
                    `;
                    barDom.onmousemove = (e) => {
                        e.stopPropagation()
                    };
                    let playerContainer = document.querySelector('#player-container-' + wndIndex);
                    playerContainer.append(barDom);
                    playerContainer.addEventListener('mouseenter', this.playerContainerMouseEnter);
                    playerContainer.addEventListener('mouseleave', this.playerContainerMouseLeave);
                    playerContainer.addEventListener('mousemove', this.playerContainerMouseMove);
                    playerContainer.addEventListener('mousedown', this.playerContainerMouseDown);
                    playerContainer.addEventListener('mouseup', this.playerContainerMouseUp);

                    let directionDom = document.createElement('div');
                    directionDom.id = 'ptz-direction-' + wndIndex;
                    directionDom.className = 'ptz-direction';
                    directionDom.innerHTML = `
                    <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
                        <path d="M427.690667 170.666667h256.64L1024 512l-339.669333 341.333333h-256.64l339.669333-341.333333-339.669333-341.333333zM0 853.333333h256.64l339.669333-341.333333-339.669333-341.333333H0l339.669333 341.333333L0 853.333333z"  fill="#f4ea2a"></path>
                    </svg>
                    `;
                    playerContainer.append(directionDom);

                    let barPtzBtn = document.querySelector('#barPtzBtn-' + wndIndex);
                    barPtzBtn.onclick = (ptzBtnEvent) => {
                        let ptzActive = barPtzBtn.dataset['active'];
                        let ptzBtnPath = barPtzBtn.querySelector('svg path')
                        let dirDom = document.querySelector('#ptz-direction-' + wndIndex);
                        let videoAndCanvasDoms = document.querySelectorAll('#player-container-' + wndIndex + ' video,#player-container-' + wndIndex + ' canvas');
                        if (ptzActive === '1') {
                            barPtzBtn.dataset['active'] = '0';
                            ptzBtnPath.style.fill = '#ffffff';
                            this.ptzWndIndex = -1;
                            this.ptzWndIndexes.delete(wndIndex);
                            dirDom.classList.remove('active');
                            videoAndCanvasDoms.forEach(d => d.classList.remove('hide-mouse'));
                        } else {
                            barPtzBtn.dataset['active'] = '1';
                            ptzBtnPath.style.fill = '#ff1111';
                            this.ptzWndIndexes.add(wndIndex);
                            dirDom.classList.add('active');
                            videoAndCanvasDoms.forEach(d => d.classList.add('hide-mouse'));
                        }
                    };
                    barPtzBtn.onmousedown = (mde) => {
                        mde.stopPropagation();
                    };
                    barPtzBtn.onmouseup = (mue) => {
                        mue.stopPropagation();
                    };
                }
            }
        },
        /**
         * 移除窗口底部工具栏
         * @param wndIndex
         */
        removePlayerWndBar(wndIndex) {
            console.log('移除 播放窗口 工具栏', wndIndex)
            if (this.needPTZ) {
                let dirDom = document.querySelector('#ptz-direction-' + wndIndex);
                if (dirDom) {
                    dirDom.remove();
                }
                let barDom = document.querySelector('#player-wnd-bar-' + wndIndex);
                if (barDom) {
                    barDom.remove();
                    let playerContainer = document.querySelector('#player-container-' + wndIndex);
                    playerContainer.removeEventListener('mouseenter', this.playerContainerMouseEnter);
                    playerContainer.removeEventListener('mouseleave', this.playerContainerMouseLeave);
                    playerContainer.removeEventListener('mousedown', this.playerContainerMouseDown);
                    playerContainer.removeEventListener('mouseup', this.playerContainerMouseUp);
                }
                let videoAndCanvasDoms = document.querySelectorAll('#player-container-' + wndIndex + ' video,#player-container-' + wndIndex + ' canvas');
                videoAndCanvasDoms.forEach(d => d.classList.remove('hide-mouse'));
                this.ptzWndIndexes.delete(wndIndex);
            }
        },
        /**
         * 鼠标进入某个播放窗口时显示工具条
         * @param e
         */
        playerContainerMouseEnter(e) {
            let wndIndex = this.getEventWndIndex(e);
            if (wndIndex >= 0) {
                let camera = this.wndIdCameraMap.get(wndIndex + 1);
                if (camera.state === 1) {
                    let barDom = document.querySelector('#player-wnd-bar-' + wndIndex);
                    if (barDom) {
                        if (barDom.className.indexOf('active') === -1) {
                            barDom.className = 'player-wnd-bar active';
                        }
                    }
                }
            }
        },
        /**
         * 鼠标离开某个播放窗口时隐藏工具条
         * @param e
         */
        playerContainerMouseLeave(e) {
            e.stopPropagation();
            let wndIndex = this.getEventWndIndex(e);
            if (wndIndex >= 0) {
                let barDom = document.querySelector('#player-wnd-bar-' + wndIndex);
                if (barDom) {
                    if (barDom.className.indexOf('active') > -1) {
                        barDom.className = 'player-wnd-bar';
                    }
                }
            }
        },
        /**
         * 鼠标在某个播放窗口的移动事件
         * @param e
         */
        playerContainerMouseMove(e) {
            let wndIndex = this.getEventWndIndex(e);
            if (wndIndex >= 0 && this.ptzWndIndexes.has(wndIndex)) {
                let ag = this.calcMouseDeg(e, wndIndex);
                //console.log(ag);
                let directionDom = document.querySelector('#ptz-direction-' + wndIndex);
                directionDom.style.left = (e.offsetX - 10) + 'px';
                directionDom.style.top = (e.offsetY - 10) + 'px';
                document.querySelector('#ptz-direction-' + wndIndex + ' svg').style.transform = 'rotate(' + ag + 'deg)';
            }
        },
        /**
         * 鼠标在某个播放窗口的按下事件
         * @param e
         */
        playerContainerMouseDown(e) {
            let wndIndex = this.getEventWndIndex(e);
            if (wndIndex >= 0 && this.ptzWndIndexes.has(wndIndex)) {
                let camera = this.wndIdCameraMap.get(wndIndex + 1);
                if (camera) {
                    let direction = this.getMouseDirection(e, wndIndex);
                    if (direction) {//回调外部传过来的云台开始控制方法
                        this.ptzStartFunc(camera.cameraIndexCode, direction);
                    }
                }
            }
        },
        /**
         * 鼠标在某个播放窗口的松开事件
         * @param e
         */
        playerContainerMouseUp(e) {
            let wndIndex = this.getEventWndIndex(e);
            if (wndIndex >= 0 && this.ptzWndIndexes.has(wndIndex)) {
                let camera = this.wndIdCameraMap.get(wndIndex + 1);
                if (camera) {
                    let direction = this.getMouseDirection(e, wndIndex);
                    if (direction) {//回调外部传过来的云台结束控制方法
                        this.ptzEndFunc(camera.cameraIndexCode, direction);
                    }
                }
            }
        },
        /**
         * 获取鼠标在某个窗口中的 方向
         * @param e
         * @param wndIndex
         * @return {string}
         */
        getMouseDirection(e, wndIndex) {
            let ag = this.calcMouseDeg(e, wndIndex);
            let directionAngles = this.calcWndEightDirectionAngle(wndIndex);
            let targetDirection = '';
            for (let direction in directionAngles) {
                let angles = directionAngles[direction];
                let inAngle = false;
                for (let degObj of angles) {
                    if (ag > degObj.start && ag <= degObj.end) {
                        inAngle = true;
                        break;
                    }
                }
                if (inAngle) {
                    targetDirection = direction;
                    break;
                }
            }
            return targetDirection;
        },
        /**
         * 计算某个播放区域鼠标相对中心点的角度
         * @param e
         * @param wndIndex
         * @return {number}
         */
        calcMouseDeg(e, wndIndex) {
            let playerContainer = document.querySelector('#player-container-' + wndIndex);
            let bcr = playerContainer.getBoundingClientRect();
            //console.log(e.offsetX, e.offsetY);
            return this.calcDeg(e.offsetX, e.offsetY, bcr.width / 2, bcr.height / 2);
        },
        /**
         * 计算某个窗口8个方向的角度
         * @param wndIndex
         */
        calcWndEightDirectionAngle(wndIndex) {
            let playerContainer = document.querySelector('#player-container-' + wndIndex);
            let bcr = playerContainer.getBoundingClientRect();
            let {width, height} = bcr;
            let centerX = width / 2;
            let centerY = height / 2;
            let topStartDeg = this.calcDeg(width / 4, 0, centerX, centerY);
            let topEndDeg = this.calcDeg(width / 4 * 3, 0, centerX, centerY);
            let topRightStartDeg = topEndDeg;
            let topRightEndDeg = this.calcDeg(width, height / 4, centerX, centerY);
            let rightStartDeg = topRightEndDeg;
            let rightEndDeg = this.calcDeg(width, height / 4 * 3, centerX, centerY);
            let bottomRightStartDeg = rightEndDeg;
            let bottomRightEndDeg = this.calcDeg(width / 4 * 3, height, centerX, centerY);
            let bottomStartDeg = bottomRightEndDeg;
            let bottomEndDeg = this.calcDeg(width / 4, height, centerX, centerY);
            let bottomLeftStartDeg = bottomEndDeg;
            let bottomLeftEndDeg = this.calcDeg(0, height / 4 * 3, centerX, centerY);
            let leftStartDeg = bottomLeftEndDeg;
            let leftEndDeg = this.calcDeg(0, height / 4, centerX, centerY);
            let topLeftStartDeg = leftEndDeg;
            let topLeftEndDeg = topStartDeg;
            return {
                UP: [{start: topStartDeg, end: topEndDeg}],
                RIGHT_UP: [{start: topRightStartDeg, end: topRightEndDeg}],
                RIGHT: [{start: rightStartDeg, end: rightEndDeg}],
                RIGHT_DOWN: [{start: bottomRightStartDeg, end: bottomRightEndDeg}],
                DOWN: [{start: bottomStartDeg, end: bottomEndDeg}],
                LEFT_DOWN: [{start: bottomLeftStartDeg, end: bottomLeftEndDeg}],
                LEFT: [{start: leftStartDeg, end: 360}, {start: -360, end: leftEndDeg}],  //比较特殊，跨两段
                LEFT_UP: [{start: topLeftStartDeg, end: topLeftEndDeg}],
            };
        },
        calcDeg(pointX, pointY, centerX, centerY) {
            return Math.atan2(pointY - centerY, pointX - centerX) * 180 / (Math.PI);
        },
        /**
         * 获取事件窗口编号
         * @param e
         * @return {number}
         */
        getEventWndIndex(e) {
            let playerContainerEL = null;
            //console.log(e.target.nodeName)
            if (e.target.className && e.target.className.indexOf('sub-wnd') > -1) {
                playerContainerEL = e.target;
            } else if (e.target.parentNode.className.indexOf('sub-wnd') > -1) {
                playerContainerEL = e.target.parentNode;
            }
            if (playerContainerEL) {
                let playerContainerId = playerContainerEL.id;
                if (playerContainerId) {
                    let wndIndex = Number(playerContainerId.replace('player-container-', ''));
                    if (wndIndex !== undefined) {
                        return wndIndex;
                    }
                }
            }
            return -1;
        },
    }
}

/**
 * h5(websocket)播放组件
 * @private
 */
const _iscVideoH5Player = {
    props: {
        urlFunc: {
            type: Function,
            required: true
        },
    },
    data() {
        return {
            containerId: 'iscH5Player',
            playerLayout: [{num: 1, layout: "1x1"}, {num: 4, layout: "2x2"}, {num: 9, layout: "3x3"}, {
                num: 16,
                layout: "4x4"
            }],
            pluginLoaded: false,
            playerMsg: '视频控件加载中，请稍后',

            wndRetryData: new Map(),    //某个播放窗口的重试数据
        }
    },
    computed: {
        /**
         * 窗口布局列数
         * @return {number}
         */
        columnNum() {
            return parseInt(this.realLayout.split('x')[0]);
        },
        /**
         * 窗口布局行数
         * @return {number}
         */
        rowNum() {
            return parseInt(this.realLayout.split('x')[1]);
        },
        innerStyle() {
            let style = {};
            let columnStyle = '';
            for (let i = 0; i < this.columnNum; i++) {
                columnStyle += '1fr ';
            }
            columnStyle = columnStyle.trim();
            let rowStyle = '';
            for (let i = 0; i < this.rowNum; i++) {
                rowStyle += '1fr ';
            }
            rowStyle = rowStyle.trim();
            style.gridTemplateColumns = columnStyle;
            style.gridTemplateRows = rowStyle;
            return style;
        },
        /**
         * 是否存在至少一个视频设备在预览(不管是加载、还是正常播放)
         */
        cameraPlaying() {
            let camera = this.cameraList.find(camera => camera.state !== 0);
            return !!camera;
        },
        /**
         * 是否能抓图
         * @return {(function(): (*|null))|*|boolean}
         */
        canCapture() {
            return this.selectedCamera && this.selectedCamera.state === 1;
        }
    },
    template: `
    <div class="isc-common-player isc-h5-player" ref="iscH5Player">
        <div class="video-container" v-show="pluginLoaded">
            <div class="h5-player-inner" :id="containerId"></div>
            <div class="h5-player-bar" v-if="curPlayMode===0 && showToolbar"><!-- 预览状态下的工具条 -->
                <div class="bar-buttons bar-left">
                    <div class="bar-btn" :class="{active: canCapture}" title="抓图" @click="captureBtnHandler">
                        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" p-id="13090" width="16" height="16">
                            <path d="M846.525959 288.822035 733.689055 288.822035l-48.477085-116.694769c-11.257391-27.103288-37.495986-44.61615-66.845431-44.61615L408.757614 127.511115c-29.348422 0-55.587016 17.512862-66.845431 44.61615l-48.475038 116.694769L180.598194 288.822035c-62.71946 0-113.7456 51.02614-113.7456 113.7456l0 378.463658c0 62.71946 51.02614 113.7456 113.7456 113.7456l665.927765 0c62.71946 0 113.7456-51.02614 113.7456-113.7456L960.27156 402.567635C960.27156 339.847152 909.24542 288.822035 846.525959 288.822035zM918.909523 781.030269c0 39.912012-32.471552 72.383564-72.383564 72.383564L180.598194 853.413833c-39.913035 0-72.383564-32.471552-72.383564-72.383564L108.21463 402.567635c0-39.912012 32.470529-72.383564 72.383564-72.383564l126.641305 0c8.357342 0 15.892969-5.02955 19.098987-12.748349l53.770648-129.443118c4.824889-11.614525 16.070001-19.120476 28.64848-19.120476l209.608925 0c12.57848 0 23.823592 7.504928 28.646434 19.1215l53.772694 129.443118c3.207042 7.716752 10.742668 12.747325 19.10001 12.747325l126.641305 0c39.912012 0 72.383564 32.471552 72.383564 72.383564L918.910547 781.030269z" fill="#ffffff"></path>
                            <path d="M513.561565 349.83769c-115.843378 0-210.089879 94.245478-210.089879 210.089879s94.245478 210.089879 210.089879 210.089879 210.089879-94.245478 210.089879-210.089879S629.405967 349.83769 513.561565 349.83769zM513.561565 728.655412c-93.036953 0-168.727843-75.691913-168.727843-168.727843s75.69089-168.727843 168.727843-168.727843c93.03593 0 168.727843 75.691913 168.727843 168.727843S606.598518 728.655412 513.561565 728.655412z" fill="#ffffff"></path>
                            <path d="M456.241069 644.973524c-28.319999-19.131733-45.227064-50.924833-45.227064-85.046978 0-11.42112-9.258874-20.681018-20.681018-20.681018s-20.681018 9.259898-20.681018 20.681018c0 47.879474 23.713075 92.484368 63.433728 119.319549 3.549849 2.397606 7.574512 3.546779 11.559267 3.546779 6.638187 0 13.159718-3.190669 17.154705-9.106402C468.193285 664.222937 465.704604 651.368164 456.241069 644.973524z" fill="#ffffff"></path>
                            <path d="M263.747963 381.095601l-86.860277 0c-11.422144 0-20.681018 9.258874-20.681018 20.681018s9.258874 20.681018 20.681018 20.681018l86.860277 0c11.422144 0 20.681018-9.258874 20.681018-20.681018S275.170107 381.095601 263.747963 381.095601z" fill="#ffffff"></path>
                        </svg>
                    </div>
                </div>
                <div class="bar-buttons bar-right">
                    <div class="bar-btn" :class="{active: cameraPlaying}" title="全部关闭" @click="allStopBtnHandler">
                        <svg viewBox="0 0 1110 1024" xmlns="http://www.w3.org/2000/svg" width="17.34375" height="16" xmlns:xlink="http://www.w3.org/1999/xlink">
                            <path d="M166.609419 0h777.510621c92.005423 0 166.609419 68.772666 166.609419 153.650908v716.698184c0 84.878243-74.603995 153.650908-166.609419 153.650908H166.609419c-92.005423 0-166.609419-68.772666-166.609419-153.650908V153.650908C0 68.772666 74.603995 0 166.609419 0zM83.304709 319.982645h944.12004V153.558348c0.185122-39.708578-32.488837-73.030462-75.344481-76.825455H166.609419c-43.040767-0.092561-79.139474 29.989695-83.30471 69.420592v173.82916z m186.69511-75.899846a55.536473 55.536473 0 1 1-78.491548-78.58411 55.536473 55.536473 0 0 1 78.491548 78.491549z m333.218838 0a55.536473 55.536473 0 1 1-78.398988-78.491549 55.536473 55.536473 0 0 1 78.491549 78.491549z m-245.100967 0a55.536473 55.536473 0 1 0 78.584109-78.491549 55.536473 55.536473 0 0 0-78.584109 78.491549z m586.00235 703.091747c46.002712 0 83.304709-34.432613 83.304709-76.825454V396.715538H83.304709v473.633554c0 42.392841 37.301998 76.825454 83.30471 76.825454h777.510621z" fill="#ffffff"></path>
                            <path d="M629.783603 475.57733l203.078369 203.17093 203.17093-203.17093a43.688692 43.688692 0 1 1 61.738046 61.830606L894.692579 740.486306l203.078369 203.078369a43.688692 43.688692 0 1 1-61.738046 61.830607L832.861972 802.224351l-203.078369 203.170931a43.688692 43.688692 0 0 1-61.738046-61.830607L771.031366 740.486306 568.045557 537.407936a43.688692 43.688692 0 0 1 61.738046-61.830606z" fill="#ffffff"></path>
                        </svg>
                    </div>
                    <div class="bar-btn active" title="进入全屏模式" @click="fullScreenDisplay">
                        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
                            <path d="M674.414 394.644l253.292-252.418-0.868 143.044c-0.348 11.864 9.244 22.327 21.106 21.979h15.177c11.864-0.349 21.804-7.326 21.979-19.364l0.699-212.995c0-0.175 0.348-11.338 0.348-11.338 0.175-5.929-1.222-11.338-5.060-15.177-3.838-3.838-9.067-6.279-15.177-6.106l-10.816 0.175c-0.174 0-0.344 0-0.52 0.175l-211.252-0.875c-11.864 0.349-21.804 10.122-21.979 22.156v15.177c1.747 14.129 12.907 22.329 24.77 21.979l139.205 0.349-252.593 251.549c-11.514 11.514-11.514 30.177 0 41.689 11.514 11.688 30.18 11.688 41.693 0h-0.004zM355.18 632.585l-253.465 251.551 0.873-142.352c0.349-11.859-9.248-22.323-21.11-21.979h-16.046c-11.864 0.349-21.804 7.327-21.979 19.365l-0.699 213.17c0 0.175-0.348 11.338-0.348 11.338-0.175 5.936 1.222 11.338 5.055 15.177 3.838 3.838 9.073 6.279 15.177 6.106l10.815-0.175c0.175 0 0.349 0 0.525-0.175l212.125 0.874c11.859-0.349 21.804-10.117 21.979-22.156v-15.176c-1.746-14.129-12.91-22.329-24.775-21.979l-139.206-0.349 252.419-251.543c11.514-11.514 11.514-30.181 0-41.693-11.334-11.688-29.824-11.688-41.34 0.001v0zM985.799 952.339l-0.524-213.17c-0.349-11.865-10.122-19.016-21.98-19.365h-15.176c-11.865-0.344-21.283 10.122-21.111 21.979l0.874 143.049-253.466-252.247c-11.514-11.514-30.18-11.514-41.693 0-11.509 11.513-11.509 30.18 0 41.693l252.422 251.542-139.205 0.349c-11.864-0.349-22.852 8.024-24.774 21.979v15.177c0.348 11.864 10.122 21.804 21.979 22.156l211.253-0.874c0.174 0 0.348 0.175 0.524 0.175l10.816 0.175c5.928 0.174 11.338-2.093 15.176-6.106 3.838-3.838 5.232-9.243 5.056-15.177 0 0-0.175-11.164-0.175-11.338h0.005zM144.282 101.407l139.205-0.349c11.859 0.349 22.848-8.024 24.77-21.979v-15.179c-0.349-11.864-10.117-21.804-21.979-22.156l-212.296 0.874c-0.175 0-0.349-0.175-0.525-0.175l-10.814-0.175c-5.936-0.174-11.338 2.093-15.177 6.106-3.838 3.838-5.237 9.243-5.055 15.177 0 0 0.348 11.164 0.348 11.338l0.52 213.17c0.175 11.86 10.122 19.016 21.979 19.36h16.051c11.864 0.348 21.282-10.117 21.105-21.979l-0.698-142.346 253.296 251.725c11.508 11.514 30.176 11.514 41.688 0 11.514-11.514 11.514-30.18 0-41.693l-252.419-251.716zM144.282 101.407z" fill="#ffffff"></path>
                        </svg>
                    </div>
                </div>
            </div>
        </div>
        <div v-if="!pluginLoaded" class="player-msg">{{playerMsg}}</div>
    </div>
    `,
    mounted() {
        this.loadRequiredScripts(false);
    },
    methods: {
        /**
         * 初始播放器及事件
         */
        initPlayer() {
            return new Promise((resolve, reject) => {
                if (this.urlFunc) {
                    this.pluginLoaded = true;
                    this.$nextTick(() => {
                        let playerRect = this.$refs.iscH5Player.getBoundingClientRect();
                        this.width = playerRect.width;
                        this.height = playerRect.height;
                        let szBasePath = this.scriptLibPath.replace(document.location.origin, '') + 'h5/';
                        //console.log('szBasePath ==>', szBasePath)
                        this.oWebControl = new window.JSPlugin({
                            szId: this.containerId,
                            szBasePath: szBasePath,
                            iMaxSplit: 4,   //最大只支持4
                            iCurrentSplit: this.rowNum,
                            oStyle: {
                                border: '#000000',
                                borderSelect: '#ffcc00',
                                background: "#262626",
                                //borderWidth: 10,
                            }
                        });
                        let thiz = this;
                        this.oWebControl.JS_SetWindowControlCallback({
                            windowEventSelect: function (iWndIndex) {  //插件选中窗口回调
                                thiz.selectWndId = iWndIndex + 1;
                            },
                            pluginErrorHandler: function (iWndIndex, iErrorCode, oError) {  //插件错误回调
                                //console.log('pluginError callback: ', iWndIndex, iErrorCode, oError);
                                if (thiz.curPlayMode === 0) {
                                    thiz.handlePreviewError(iWndIndex, iErrorCode);
                                }
                            },
                            firstFrameDisplay: function (iWndIndex, iWidth, iHeight) {  //首帧显示回调
                                thiz.removePlayerWndMsg(iWndIndex);
                                thiz.initPlayerWndBar(iWndIndex);
                            },
                            windowEventOver: function (iWndIndex) {  //鼠标移过回调
                            },
                            windowEventOut: function (iWndIndex) {  //鼠标移出回调
                            },
                            windowEventUp: function (iWndIndex) {  //鼠标mouseup事件回调
                            },
                            windowFullCcreenChange: function (bFull) {  //全屏切换回调
                            },
                            performanceLack: function () {  //性能不足回调
                            }
                        });
                        window.oWebControl = this.oWebControl;
                        resolve();
                    });
                } else {
                    this.playerMsg = '缺少获取播放流的函数：url-func';
                    reject();
                }
            });
        },
        /**
         * 预览播放错误处理
         * @param wndIndex
         * @param errorCode
         */
        handlePreviewError(wndIndex, errorCode) {
            switch (errorCode) {
                case '0x12f910011': //流中断，电脑配置过低，程序卡主线程都可能导致流中断
                    let camera = this.wndIdCameraMap.get(wndIndex + 1);
                    if (camera) { //尝试重新取流播放
                        this.doPreview(camera.cameraIndexCode, wndIndex + 1).then(() => {
                        });
                    }
                    break;
                case '0x12f900009': //取流超时错误
                case '0x12f910000': //websocket连接失败，请检查网络是否通畅，URL是否正确
                case '0x12f910010': //取流失败
                case '0x12f910015': //未找到对应websocket，取流套接字被动关闭的报错
                case '0x12f910016': //websocket不在连接状态
                case '0x12f910019': //wss连接失败，原因：端口尚未开通、证书未安装、证书不安全
                case '0x12f910023': //ws/wss连接超时，默认6s超时时间，原因：网络异常，网络不通
                case '0x12f910027': //后端取流超时，主动关闭连接（设备突然离线或重启，网络传输超时20s）
                case '0x01b01307': //取流失败，详情根据错误码在运管后台进行查询
                    let retryData = this.wndRetryData.get(wndIndex);
                    if (retryData) { //存在重试数据，重试播放
                        let lastTryTime = retryData.tryTime;    //上一次尝试播放时间戳
                        let curTime = new Date().getTime();
                        let costTime = curTime - lastTryTime;
                        retryData.tryCount++;
                        if (costTime < 5000) {
                            retryData.timer = setTimeout(() => {
                                this.retryPreview(wndIndex).then(() => {
                                });
                            }, 5000 - costTime);  //至少等待5秒再重试
                        } else {
                            this.retryPreview(wndIndex).then(() => {
                            });
                        }
                    }
                    break;
                default:

                    break;
            }
        },
        fullScreenDisplay() {
            if (this.oWebControl) {
                this.oWebControl.JS_FullScreenDisplay(true).then(
                    () => {
                    },
                    () => {
                    }
                );
            }
        },
        /**
         * 重新设置播放网格布局
         */
        resetLayout() {
            this.arrangeWindow(this.rowNum).then(() => {
            });
        },
        /**
         * 取流协议
         * @return {string}
         */
        getPlayProtocol() {
            return location.protocol === 'https:' ? 'wss' : 'ws';
        },
        /**
         * 在指定窗口显示消息
         * @param wndIndex 下标从0开始
         * @param msg
         * @param color
         * @param loading
         */
        showPlayerWndMsg(wndIndex, msg, color = '#ffffff', loading = true) {
            let msgDom = document.querySelector('#player-wnd-msg-' + wndIndex);
            if (msgDom) {
                let loadingDom = msgDom.querySelector('#player-wnd-msg-loading-' + wndIndex);
                if (loading) {
                    loadingDom.style.display = 'block';
                } else {
                    loadingDom.style.display = 'none';
                }
                msgDom.querySelector('#player-wnd-msg-inner-' + wndIndex).innerHTML = msg;
            } else {
                msgDom = document.createElement('div');
                msgDom.id = 'player-wnd-msg-' + wndIndex;
                msgDom.className = 'player-wnd-msg';
                msgDom.style.color = color;
                let html = '';
                if (loading) {
                    html += `
                        <div id="player-wnd-msg-loading-${wndIndex}">
                            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="30px" viewBox="0 0 24 30" style="enable-background:new 0 0 50 50;" xml:space="preserve">
                                <rect x="0" y="10" width="4" height="10" fill="#FF6700" opacity="0.2">
                                  <animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0s" dur="0.6s" repeatCount="indefinite" />
                                  <animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0s" dur="0.6s" repeatCount="indefinite" />
                                  <animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0s" dur="0.6s" repeatCount="indefinite" />
                                </rect>
                                <rect x="8" y="10" width="4" height="10" fill="#FF6700"  opacity="0.2">
                                  <animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0.15s" dur="0.6s" repeatCount="indefinite" />
                                  <animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0.15s" dur="0.6s" repeatCount="indefinite" />
                                  <animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0.15s" dur="0.6s" repeatCount="indefinite" />
                                </rect>
                                <rect x="16" y="10" width="4" height="10" fill="#FF6700"  opacity="0.2">
                                  <animate attributeName="opacity" attributeType="XML" values="0.2; 1; .2" begin="0.3s" dur="0.6s" repeatCount="indefinite" />
                                  <animate attributeName="height" attributeType="XML" values="10; 20; 10" begin="0.3s" dur="0.6s" repeatCount="indefinite" />
                                  <animate attributeName="y" attributeType="XML" values="10; 5; 10" begin="0.3s" dur="0.6s" repeatCount="indefinite" />
                                </rect>
                            </svg>
                        </div>
                    `;
                }
                html += '<div id="player-wnd-msg-inner-' + wndIndex + '" style="margin-top: 20px;user-select: none;">' + msg + '</div>';
                msgDom.innerHTML = html;
                let playerContainer = document.querySelector('#player-container-' + wndIndex);
                playerContainer.append(msgDom);
            }
        },
        /**
         * 移除窗口消息
         * @param wndIndex
         */
        removePlayerWndMsg(wndIndex) {
            let msgDom = document.querySelector('#player-wnd-msg-' + wndIndex);
            if (msgDom) {
                msgDom.remove();
            }
        },
        /**
         * 在预览指定摄像头前，用来做一些清理操作等
         * @param cameraIndexCode
         * @param wndId
         * @return {Promise<boolean>}
         */
        async beforePreview(cameraIndexCode, wndId) {
            if (this.curPlayMode === 1) {  //处于回放状态，销毁回放，初始预览
                await this.doStopAll();
                this.removePlayerWndMsg(0);//回放只有1个窗口，直接指定0
                await this.arrangeWindow(this.rowNum);
                this.curPlayMode = 0;
            }
            return true;
        },
        /**
         * 真正开始尝试预览某个摄像头
         * @param cameraIndexCode
         * @param playWndId
         * @return {Promise<boolean>}
         */
        async doPreview(cameraIndexCode, playWndId, isPlayback, startTime, endTime) {
            let protocol = this.getPlayProtocol();
            let curIndex = playWndId - 1;
            try {
                let playUrl = await this.urlFunc(cameraIndexCode, protocol);
                if (playUrl && /^wss?:\/\/.+/i.test(playUrl)) {
                    if (location.protocol === "https:") {
                        playUrl = playUrl.replace("36.26.71.66", "isc.tp9800.cn")
                    }
                    let retryData = {playUrl, wndIndex: curIndex, cameraIndexCode, tryCount: 0};
                    this.wndRetryData.set(curIndex, retryData);
                    return this.retryPreview(curIndex, isPlayback, startTime, endTime);
                } else {
                    this.showPlayerWndMsg(curIndex, '视频流获取失败');
                    return false;
                }
            } catch (e) {
                this.showPlayerWndMsg(curIndex, '视频流获取失败');
                return false;
            }
        },
        /**
         * 调用控件进行预览，以及预览失败时的重试逻辑
         * @param wndIndex
         * @return {Promise<unknown>}
         */
        retryPreview(wndIndex, isPlayback, startTime, endTime) {
            return new Promise(resolve => {
                let retryData = this.wndRetryData.get(wndIndex);
                if (retryData) {
                    retryData.tryTime = new Date().getTime();
                    let tryCount = retryData.tryCount;
                    if (tryCount === 0) {
                        this.showPlayerWndMsg(wndIndex, '设备连接中，请稍后......');
                    } else {
                        this.showPlayerWndMsg(wndIndex, '尝试第' + (tryCount) + '次重连，请稍后......');
                    }
                    if (isPlayback) {
                        this.oWebControl.JS_Play(retryData.playUrl, {
                            playURL: retryData.playUrl,
                            mode: 0
                        }, wndIndex, startTime, endTime).then(
                            () => {
                                this.wndRetryData.delete(wndIndex);
                                this.changeCameraState(retryData.cameraIndexCode, 1, wndIndex + 1);
                                resolve(true);
                            },
                            (e) => {
                                resolve(false);
                            }
                        );
                    } else {
                        this.oWebControl.JS_Play(retryData.playUrl, {
                            playURL: retryData.playUrl,
                            mode: 0
                        }, wndIndex).then(
                            () => {
                                this.wndRetryData.delete(wndIndex);
                                this.changeCameraState(retryData.cameraIndexCode, 1, wndIndex + 1);
                                resolve(true);
                            },
                            (e) => {
                                resolve(false);
                            }
                        );
                    }
                } else {
                    resolve(false);
                }
            });
        },
        /**
         * 真正开始尝试批量预览
         * @param cameraIndexCodeWndIdArr [{cameraIndexCode, wndId}]
         * @return {Promise<boolean>}
         */
        async doBatchPreview(cameraIndexCodeWndIdArr, isPlayback) {
            cameraIndexCodeWndIdArr.forEach(codeAndWndId => {
                let {cameraIndexCode, wndId} = codeAndWndId;
                if (isPlayback) {
                    let objs = this.cameraList.find(item => {
                        return item.cameraIndexCode = cameraIndexCode
                    });
                    let {startTime2, endTime2} = objs;
                    this.doPreview(cameraIndexCode, wndId, isPlayback, startTime2, endTime2);
                } else {
                    this.doPreview(cameraIndexCode, wndId);
                }

            });
            setTimeout(() => {
                let lastObj = cameraIndexCodeWndIdArr[cameraIndexCodeWndIdArr.length - 1];
                if (this.selectWndId === lastObj.wndId) {   //h5 控件下，如果最后一个 就是之前选中的的窗口，不会触发窗口选中事件，所以此处触发一下选中视频设备事件
                    this.emitSelectCamera(lastObj.cameraIndexCode);
                } else {
                    this.selectWnd(lastObj.wndId - 1);
                }
            }, 200);
            return true;
        },
        /**
         * 停止预览操作
         * @param cameraIndexCode
         * @return {Promise<boolean>}
         */
        async doStopPreview(cameraIndexCode) {
            return new Promise((resolve, reject) => {
                let camera = this.cameraCodeMap.get(cameraIndexCode);
                if (camera && camera._wndId) {
                    let wndIndex = camera._wndId - 1;
                    this.clearRetryData(wndIndex);
                    this.removePlayerWndBar(wndIndex);
                    this.oWebControl.JS_Stop(wndIndex).then(
                        () => {
                            resolve(true);
                        },
                        () => {
                            resolve(true);
                        }
                    );
                } else {
                    resolve(true);
                }
            });
        },
        /**
         * 清除某个窗口的重试数据
         * @param wndIndex
         */
        clearRetryData(wndIndex) {
            let retryData = this.wndRetryData.get(wndIndex);
            if (retryData) {
                if (retryData.timer) {
                    window.clearTimeout(retryData.timer)
                }
                this.wndRetryData.delete(wndIndex);
            }
        },
        /**
         * 在回放指定摄像头前，用来做一些清理操作等
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @return {Promise<boolean>}
         */
        async beforePlayback(cameraIndexCode) {
            await this.doStopAll();
            //console.log('doStopAll')
            if (this.curPlayMode === 0) {  //处于预览状态，销毁预览，初始回放
                await this.arrangeWindow(1);
                await this.selectWnd(0);
                this.curPlayMode = 1;
                await this.resizePlayer();
            }
            return true;
        },
        /**
         * 调用控件自适应容器大小
         * @return {Promise<unknown>}
         */
        resizePlayer() {
            return new Promise(resolve => {
                this.$nextTick(() => {
                    if (this.oWebControl) {
                        this.oWebControl.JS_Resize().then(
                            () => {
                                resolve();
                            },
                            (err) => {
                                resolve();
                            }
                        );
                    } else {
                        resolve();
                    }
                });
            });
        },
        /**
         * 调用控件设置分屏
         * @param splitNum
         * @return {Promise<unknown>}
         */
        arrangeWindow(splitNum) {
            return new Promise(resolve => {
                if (this.oWebControl) {
                    this.oWebControl.JS_ArrangeWindow(splitNum).then(
                        () => {
                            resolve();
                        },
                        (err) => {
                            resolve();
                        }
                    );
                } else {
                    resolve();
                }
            });
        },
        /**
         * 修复控件样式问题，暂未使用
         * @param splitNum
         */
        resetPlayerStyle(splitNum) {
            let parentWnd = document.querySelector('#' + this.containerId + ' .parent-wnd');
            parentWnd.style.display = 'grid';
            parentWnd.style.gridTemplateColumns = 'repeat(' + splitNum + ', 1fr)';
            parentWnd.style.gridTemplateRows = 'repeat(' + splitNum + ', 1fr)';
            parentWnd.querySelectorAll('.sub-wnd').forEach(subWnd => {
                subWnd.style.float = 'inherit';
                subWnd.style.width = 'auto!important';
                subWnd.style.height = 'auto!important';
            });
        },
        /**
         * 调用控件选中某个窗口
         * @param wndIndex
         * @return {Promise<unknown>}
         */
        selectWnd(wndIndex) {
            return new Promise(resolve => {
                if (this.oWebControl) {
                    this.oWebControl.JS_SelectWnd(wndIndex).then(
                        () => {
                            resolve();
                        },
                        (err) => {
                            resolve();
                        }
                    );
                } else {
                    resolve();
                }
            });
        },
        /**
         * 真正开始尝试回放某个摄像头
         * 由各自播放方式实现
         * @param cameraIndexCode
         * @param statTimestamp
         * @param endTimestamp
         * @return {Promise<boolean>}
         */
        async doPlayback(cameraIndexCode, statTimestamp, endTimestamp) {
            this.showPlayerWndMsg(0, 'H5 暂不支持回放', null, false);
            throw new Error('H5 暂不支持回放');
        },
        /**
         * 停止所有播放(预览、回放)
         * @return {Promise<unknown>}
         */
        doStopAll() {
            return new Promise((resolve, reject) => {
                if (this.oWebControl) {
                    this.oWebControl.JS_StopRealPlayAll().then(
                        () => {
                            if (this.curPlayMode === 0) {  //预览状态下，
                                this.cameraList.forEach(camera => {//改变所有设备为待机状态
                                    this.changeCameraState(camera.cameraIndexCode, 0, 0);
                                });
                                this.wndRetryData.clear();  //清除重试数据
                                for (let i = 0; i < this.wndCount; i++) { //移除所有窗口消息、工具栏
                                    this.removePlayerWndMsg(i);
                                    this.removePlayerWndBar(i);
                                }
                                this.ptzWndIndexes.clear();
                            }
                            resolve();
                        },
                        () => {
                            resolve()
                        }
                    );
                } else {
                    resolve();
                }
            });
        },
        /**
         * 工具栏抓图按钮点击事件
         */
        captureBtnHandler() {
            if (this.canCapture && this.oWebControl) {
                this.oWebControl.JS_CapturePicture(this.selectedCamera._wndId - 1, 'img', 'JPEG', imageData => {
                    //console.info('JS_CapturePicture success', imageData); //2.1.0开始全是base64，之前的版本存在blob或者是base64
                    if (imageData) {
                        imageData = imageData.substring(23);
                        let raw = window.atob(imageData);
                        let rawLength = raw.length;
                        let uInt8Array = new Uint8Array(rawLength);
                        for (let i = 0; i < rawLength; ++i) {
                            uInt8Array[i] = raw.charCodeAt(i);
                        }
                        let blob = new Blob([uInt8Array], {type: 'image/jpg'});
                        //保存图片
                        let aLink = document.createElement('a');
                        let evt = document.createEvent("HTMLEvents");
                        evt.initEvent("click", true, true);
                        aLink.download = new Date().getTime() + ".jpg";
                        aLink.href = URL.createObjectURL(blob);
                        aLink.click();
                        aLink.remove();
                    }
                }).then(
                    () => {
                    },
                    () => {
                    }
                );
            }
        },
        /**
         * 工具栏全部关闭按钮点击事件
         */
        allStopBtnHandler() {
            if (this.cameraPlaying) {
                this.doStopAll().then(() => {
                });
            }
        }
    },
    mixins: [_iscVideoBase, _iscVideoH5WndBar]
};
Vue.component('isc-video-h5-player', _iscVideoH5Player);

(function (root, factory) {
    root.iscUtil = factory('iscUtil');
})(window, function (iscUtil) {
    'use strict';
    const loadScript = (url) => {
        return new Promise((resolve) => {
            let script = document.createElement("script");
            script.type = "text/javascript";
            script.src = url;
            if (script.readyState) {
                script.onreadystatechange = function () {
                    if (script.readyState == "loaded" || script.readyState == "complete") {
                        script.onreadystatechange = null;
                        resolve();
                    }
                };
            } else {
                script.onload = function () {
                    resolve();
                };
            }
            document.body.appendChild(script);
        });
    };
    return {
        /**
         * 获取当前js路径
         * @return {string}
         */
        getCurJsPath: function () {
            let js = document.scripts
                , last = js.length - 1
                , src;
            for (let i = last; i > 0; i--) {
                if (js[i].src && js[i].src.indexOf('isc-video-player.js') > 0) {
                    src = js[i].src;
                    break;
                }
            }
            let jsPath = src || js[last].src;
            return jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
        },
        /**
         * 动态加载多个js
         * @param urls 数组
         * @return {Promise<unknown>}
         */
        dynamicLoadScripts: function (urls) {
            return new Promise(resolve => {
                let allPromise = urls.map(url => loadScript(url));
                Promise.all(allPromise).then(() => {
                    resolve();
                })
            });
        },
        /**
         * 动态创建组件样式
         */
        createStyle: function () {
            let css = `
            .isc-video-player, .isc-common-player{
                width: 100%;
                height: 100%;
                overflow: hidden;
                background-color: #000000;
                position: relative;
                box-sizing: border-box;
            }
            .isc-common-player .player-msg{
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                z-index: 10;
                color: red;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .isc-common-player .video-container{
                width: 100%;
                height: 100%;
            }
            .isc-h5-player .video-container{
                display: flex;
                flex-direction: column;
            }
            .isc-h5-player .h5-player-inner{
                flex: 1;
                overflow: hidden;
                box-sizing: border-box;
                padding: 1px;
            }
            .isc-h5-player .h5-player-bar{
                background-color: #3d3d3d;
                height: 40px;
                display: flex;
                align-items: center;
                justify-content: space-between;
                box-sizing: border-box;
                padding: 0 4px;
                overflow: hidden;
            }
            .isc-h5-player .h5-player-bar .bar-buttons{
                height: 100%;
                display: flex;
                align-items: center;
            }
            .isc-h5-player .h5-player-bar .bar-buttons .bar-btn{
                padding: 8px;
                filter: opacity(0.4);
            }
            .isc-h5-player .h5-player-bar .bar-buttons .bar-btn.active{
                cursor: pointer;
                filter: opacity(1);
            }
            .isc-h5-player .h5-player-bar .bar-buttons .bar-btn.active:hover{
                background-color: #515151;
            }
            .isc-h5-player .player-wnd-msg{
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                color: #ffffff;
                z-index: 99;
            }
            .isc-h5-player .player-wnd-bar{
                position: absolute;
                left: 0;
                right: 0;
                bottom: -40px;
                height: 40px;
                display: flex;
                align-items: center;
                justify-content: space-between;
                background: #1a1a1a;
                color: #ffffff;
                z-index: 99;
            }
            .isc-h5-player .player-wnd-bar.active{
                bottom: 0;
            }
            .isc-h5-player .player-wnd-bar .bar-btn{
                padding: 8px;
                cursor: pointer;
            }
            .isc-h5-player .player-wnd-bar .bar-btn.active{
                
            }
            .isc-h5-player .player-wnd-bar .bar-btn:hover{
                background-color: #515151;
            }
            .isc-h5-player .hide-mouse{
                cursor: none!important;
            }
            .isc-h5-player .ptz-direction{
                position: absolute;
                z-index: 98;
                display: none;
                pointer-events: none;
            }
            .isc-h5-player .ptz-direction.active{
                display:flex;
            }
            `;
            let head = document.getElementsByTagName('head')[0];
            let style = document.createElement('style');
            style.appendChild(document.createTextNode(css));
            head.appendChild(style);
        },
    }
});